package value;

import java.text.DecimalFormat;
import java.util.ArrayList;

import problems.Actions;

public class LazyScalarizedVector<A> extends SimpleVector<A> implements Vector, Taggable<A>  {

	public A theTag;
	public AlphaMatrix<A> theMatrix;
	public double[] theWeights;
	
	public static double[] currentScalarizationWeights;
	
	private ArrayList<AlphaMatrix<?>> matricesToProcess;
	
	public LazyScalarizedVector(int l){
		super(l);
		this.theMatrix = null;
		this.matricesToProcess=null;
		this.theTag = null;
		this.theWeights = LazyScalarizedVector.getCurrentScalarizationWeights();
	}
	
	public LazyScalarizedVector(AlphaMatrix<A> matrix, double[] wvec){
		super(matrix.getNumberOfStates());
		this.theMatrix = matrix;
		this.matricesToProcess=null;
		this.theTag = matrix.getAction();
		this.theWeights = wvec;
		this.rescalarize(this.theWeights, true);
	}
	//getAction() added to check tag value
	public LazyScalarizedVector(AlphaMatrix<A> matrix){
		super(matrix.getNumberOfStates());
		this.theMatrix = matrix;
		this.matricesToProcess=null;
		this.theTag = null;
		this.theWeights = LazyScalarizedVector.getCurrentScalarizationWeights();
		this.rescalarize(this.theWeights, true);
	}
	
	@Override
	public void remove() {
		System.err.println("Do not use LazyScalarizedVector.remove()");
	}

	@Override
	public void tag(A t) {
		this.theTag = t;
	}

	@Override
	public void tag(ArrayList<A> tl) {
		System.err.println("AlphaMatrix should only be tagged with one action in A.");
		System.err.println("Tagging with the first action in the list.");
		this.theTag = tl.get(0);
	}

	@Override
	public ArrayList<A> retrieveTags() {
		ArrayList<A> lst = new ArrayList<A>(1);
		lst.add(theTag);
		return lst;
	}

	@Override
	public LazyScalarizedVector<A> zeroVector() {
		AlphaMatrix<A> am;
		if(this.theMatrix == null){
			am=new AlphaMatrix<A>(this.matricesToProcess.get(0).zeroVector());
		} else {
			am = this.theMatrix.zeroVector();
		}
		LazyScalarizedVector<A> vec = new LazyScalarizedVector<A>(am);
		return vec;
	}

	@Override
	public LazyScalarizedVector<A> linearTransformation(double[][] trans) {
		if(this.theMatrix==null){
			this.process();
		}
		AlphaMatrix<A> am = this.theMatrix.linearTransformation(trans);
		LazyScalarizedVector<A> vec = new LazyScalarizedVector<A>(am);
		return vec;
	}

	@Override
	public void multiplyByScalar(double scalar) {
		this.process();
		for(int i=0; i<value.length; i++){
			value[i] = value[i]*scalar;
		}
		this.theMatrix.multiplyByScalar(scalar);
	}
	
	//Not overriden because of different argument
	public LazyScalarizedVector<A> add(LazyScalarizedVector<?> v2){
		double[] val = new double[this.value.length];
		for(int i=0; i<this.value.length; i++){
			val[i] = this.value[i]+v2.getValue(i);
		}
		LazyScalarizedVector<A> vec = new LazyScalarizedVector<A>(this.value.length);
		vec.setValue(val);
		vec.addMatrixToProcess(this.getMatrix());
		vec.addMatrixToProcess(v2.getMatrix());
		return vec;
	}
	
	public LazyScalarizedVector<A> add(SimpleVector<?> v2){
		try{
			return this.add((LazyScalarizedVector<?>) v2);
		} catch (Exception e) {
			System.err.println("SimpleVector should not be added to LazyScalarizedVector.");
			return null;
		}
	}
	
	public AlphaMatrix<A> getMatrix(){
		if(this.theMatrix==null){
			this.process();
		}
		return this.theMatrix;
	}
	
	public void addMatrixToProcess(AlphaMatrix<?> mat){
		if(this.theMatrix!=null){
			System.err.println("LAZYVECTOR: Matrix has already been initialized.");
			System.exit(-132);
		}
		if(this.matricesToProcess==null){
			this.matricesToProcess = new ArrayList<AlphaMatrix<?>>();	
		}
		this.matricesToProcess.add(mat);
	}
	
	@Override
	public void process(){
		if(this.matricesToProcess!=null){
			if(this.theMatrix!=null){
				System.err.println("LAZYVECTOR: Matrices not fully processed?");
				System.exit(-134);
			}
			this.theMatrix = new AlphaMatrix<A>(this.matricesToProcess.get(0));
			for(int i=1; i<this.matricesToProcess.size(); i++){
				this.theMatrix = theMatrix.add(matricesToProcess.get(i));
			}
			this.matricesToProcess=null;
		}
	}
	
	public static double[] getCurrentScalarizationWeights(){
		return LazyScalarizedVector.currentScalarizationWeights;
	}
	
	public static void setCurrentScalarizationWeights(double[] wvec){
		LazyScalarizedVector.currentScalarizationWeights = wvec;
	}
	
	public void rescalarize(double[] wvec, boolean adjustGlobal){
		if(theMatrix==null){
			this.process();
		}
		this.value = this.theMatrix.scalarizeWithWeights(wvec);
		this.theWeights = wvec;
		if(adjustGlobal){
			LazyScalarizedVector.currentScalarizationWeights = wvec;
		}
	}
	
	public void setWeightVectorNoCalc(double[] wvec){
		this.theWeights = wvec;
	}

	public String toString(String[] states, Actions<A> acts){
		String result = "LSV: Action ";
		if(this.theTag!=null){
			result+=this.theTag.toString();
		}
		DecimalFormat format = new DecimalFormat("###.##");
		result+="\n";
		for(int s=0; s<this.length(); s++){
			result+=states[s]+": ";
			result+=format.format(this.getValue(s))+"\n";
		}
		result+="Underlying matrix:\n"+this.getMatrix().toString(states,acts);
		return result;
	}
	
	@Override
	public String className() {
		return "LazyScalarizedVector";
	}
	
	public double[] getWeights(){
		return this.theWeights;
	}
	
	public LazyScalarizedVector<A> copyMatrix(AlphaMatrix<A> v) {
		return new LazyScalarizedVector<A>(v,LazyScalarizedVector.getCurrentScalarizationWeights());
	}
}
